;	skeletal cbios for first level of cp/M 2.0 alteration
;
SECTION CBIOS_REGION
SIO_REG             equ 0xDE
SIO_DATA            equ 0xDF
CF_BASE             equ 0x98	    ;BASE ADDRESS OF PRIMARY IDE CONTROLLER
CF_REG0	            equ	CF_BASE + 0	;DATA PORT
CF_REG1	            equ	CF_BASE + 1	;READ: ERROR CODE, WRITE: FEATURE
CF_REG2	            equ	CF_BASE + 2	;NUMBER OF SECTORS TO TRANSFER
CF_REG3	            equ	CF_BASE + 3	;SECTOR ADDRESS LBA 0 [0:7]
CF_REG4	            equ	CF_BASE + 4	;SECTOR ADDRESS LBA 1 [8:15]
CF_REG5	            equ	CF_BASE + 5	;SECTOR ADDRESS LBA 2 [16:23]
CF_REG6	            equ	CF_BASE + 6	;SECTOR ADDRESS LBA 3 [24:27 (LSB)]
CF_REG7	            equ	CF_BASE + 7	;READ: STATUS, WRITE: COMMAND

ccp:				equ	0DC00h		;base of ccp
bdos:				equ	0E406h		;bdos entry
bios:				equ	0F200h		;base of bios
cdisk:				equ	0004h		;address of current disk number 0=a,... l5=p
iobyte:				equ	0003h		;intel i/o byte
disks:				equ	04h			;number of disks in the system
;
	;org	bios			;origin of this program
nsects:	equ	15	;warm start sector count
;
;	jump vector for individual subroutines
;
	jp	boot	;cold start
wboote:	jp	wboot	;warm start
	jp	const	;console status
	jp	conin	;console character in
	jp	conout	;console character out
	jp	list	;list character out
	jp	punch	;punch character out
	jp	reader	;reader character out
	jp	home	;move head to home position
	jp	seldsk	;select disk
	jp	settrk	;set track number
	jp	setsec	;set sector number
	jp	setdma	;set dma address
	jp	read	;read disk
	jp	write	;write disk
	jp	listst	;return list status
	jp	sectran	;sector translate
;
;	fixed data tables for four-drive standard
;	ibm-compatible 8" disks
;	no translations
;
;	disk Parameter header for disk 00
dpbase:
	defw	0000h, 0000h
	defw	0000h, 0000h
	defw	dirbf, dpblk
	defw	chk00, all00
;	disk parameter header for disk 01
	defw	0000h, 0000h
	defw	0000h, 0000h
	defw	dirbf, dpblk
	defw	chk01, all01
;	disk parameter header for disk 02
	defw	0000h, 0000h
	defw	0000h, 0000h
	defw	dirbf, dpblk
	defw	chk02, all02
;	disk parameter header for disk 03
	defw	0000h, 0000h
	defw	0000h, 0000h
	defw	dirbf, dpblk
	defw	chk03, all03
;
;	sector translate vector
trans:
	defb	 1,  7, 13, 19	;sectors  1,  2,  3,  4
	defb	25,  5, 11, 17	;sectors  5,  6,  7,  6
	defb	23,  3,  9, 15	;sectors  9, 10, 11, 12
	defb	21,  2,  8, 14	;sectors 13, 14, 15, 16
	defb	20, 26,  6, 12	;sectors 17, 18, 19, 20
	defb	18, 24,  4, 10	;sectors 21, 22, 23, 24
	defb	16, 22		;sectors 25, 26
;
dpblk:	;disk parameter block for all disks.
	defw	26		;sectors per track
	defb	3		;block shift factor
	defb	7		;block mask
	defb	0		;null mask
	defw	242		;disk size-1
	defw	63		;directory max
	defb	192		;alloc 0
	defb	0		;alloc 1
	defw	0		;check size
	defw	2		;track offset

;
; Print message to ACIA.
;
acia_print:
    ld		a, (hl)
    inc 	hl
    or 		a
    ret 	z                   ;return if encountered 0
    ld 		c, a
acia_print_wait:
    in 		a,(SIO_REG)         ;wait until write is ready
    and 	0x02                ;mask out TDRE bit
    jp 		z,acia_print_wait
    ld 		a,c
    out 	(SIO_DATA),a
    jp 		acia_print

;
;	end of fixed tables
;
;	individual subroutines to perform each function
boot:	;simplest case is to just perform parameter initialization
	di
	ld 		sp,biosstack
	xor		a			;zero in the accum
	ld 		(iobyte),a	;clear the iobyte
	ld 		(cdisk),a	;select disk zero
	jp 		gocpm		;initialize and go to cp/m
;
wboot:	;simplest case is to read the disk until all sectors loaded
	di
	ld 		sp,biosstack

	ld 		c,0			;select disk 0
	call 	seldsk
	call 	home		;go to track 00
;
; 	ld 	b, nsects	;b counts * of sectors to load
; 	ld 	c, 0		;c has the current track number
; 	ld 	d, 2		;d has the next sector to read
; ;	note that we begin by reading track 0, sector 2 since sector 1
; ;	contains the cold start loader, which is skipped in a warm start
; 	ld	hl, ccp		;base of cp/m (initial load point)
; load1:	;load	one more sector
; 	push	bc		;save sector count, current track
; 	push	de		;save next sector to read
; 	push	hl		;save dma address
; 	ld 	c, d		;get sector address to register C
; 	call	setsec		;set sector address from register C
; 	pop	bc		;recall dma address to b, C
; 	push	bc		;replace on stack for later recall
; 	call	setdma		;set dma address from b, C
; ;
; ;	drive set to 0, track set, sector set, dma address set
; 	call	read
; 	cp	00h		;any errors?
; 	jp	nz,wboot	;retry the entire boot if an error occurs
; ;
; ;	no error, move to next sector
; 	pop	hl		;recall dma address
; 	ld	de, 128		;dma=dma+128
; 	add	hl,de		;new dma address is in h, l
; 	pop	de		;recall sector address
; 	pop	bc		;recall number of sectors remaining, and current trk
; 	dec	b		;sectors=sectors-1
; 	jp	z,gocpm		;transfer to cp/m if all have been loaded
; ;
; ;	more	sectors remain to load, check for track change
; 	inc	d
; 	ld 	a,d		;sector=27?, if so, change tracks
; 	cp	27
; 	jp	C,load1		;carry generated if sector<27
; ;
; ;	end of	current track,	go to next track
; 	ld 	d, 1		;begin with first sector of next track
; 	inc	c		;track=track+1
; ;
; ;	save	register state, and change tracks
; 	push	bc
; 	push	de
; 	push	hl
; 	call	settrk		;track address set from register c
; 	pop	hl
; 	pop	de
; 	pop	bc
; 	jp	load1		;for another sector
;
;	end of	load operation, set parameters and go to cp/m
gocpm:
	ld 		a,11001100b
	out 	(0xFF),a

	ld		hl,cpm_prompt_mesg
	call 	acia_print

	ld 		a,0c3h		;c3 is a jmp instruction
	ld 		(0),a		;for jmp to wboot
	ld 		hl,wboote	;wboot entry point
	ld 		(1),hl		;set address field for jmp at 0
;
	ld 		(5),a		;for jmp to bdos
	ld 		hl,bdos		;bdos entry point
	ld		(6),hl		;address field of Jump at 5 to bdos
;
	ld 		bc,80h		;default dma address is 80h
	call 	setdma
;
	ld 		a,(cdisk)	;get current disk number
	cp		disks		;see if valid disk number
	jp 		c,diskok	;disk valid, go to ccp
	ld 		a,0			;invalid disk, change to disk 0
diskok:
	ld 		c,a			;send to the ccp
	jp 		ccp			;go to cp/m for further processing
;
;
;	simple i/o handlers (must be filled in by user)
;	in each case, the entry point is provided, with space reserved
;	to insert your own code
;
const:	;console status, return 0ffh if character ready, 00h if not
	in 		a,(0xDE)	;get status
	and 	0x01		;check RxRDY bit
	jp 		z,no_char
	ld 		a,0ffh		;char ready	
	ret
no_char:
	ld 		a,00h		;no char
	ret
;
conin:	;console character into register a
	in 		a,(0xDE)	;get status
	and 	0x01		;check RxRDY bit
	jp 		z,conin		;loop until char ready
	in 		a,(0xDF)	;get char
	and		7fh			;strip parity bit
	ret
;
conout:	;console character output from register c	
	in		a,(0xDE)
	and 	0x02		;mask out TDRE bit
	jp		z,conout	;loop until port ready
	ld		a,c			;get the char
	out		(0xDF),a	;out to port
	ret
;
list:	;list character from register c
	ld 		a,c	  		;character to register a
	ret		  	  		;null subroutine
;
listst:	;return list status (0 if not ready, 1 if ready)
	xor		a	 		;0 is always ok to return
	ret
;
punch:	;punch	character from	register C
	ld 		a,c			;character to register a
	ret					;null subroutine
;
;
reader:	;reader character into register a from reader device
	ld 		a,1ah		;enter end of file for now (replace later)
	and 	7fh			;remember to strip parity bit
	ret
;
;
;	i/o drivers for the disk follow
;	for now, we will simply store the parameters away for use
;	in the read and write	subroutines
;
home:	;move to the track 00	position of current drive
;		translate this call into a settrk call with Parameter 00
	ld 		c,0			;select track 0
	call 	settrk
	ret					;we will move to 00 on first read/write
;
seldsk:					;select disk given by register c	
	ld 		hl,0000h	;error return code
	ld 		a,c
	ld 		(diskno),a
	cp 		disks		;must be between 0 and 3
	ret	nc				;no carry if 4, 5,...
;	disk number is in the proper range
;	defs	10		;space for disk select
;	compute proper disk Parameter header address

	ld 		a,(diskno)
	ld 		l,a			;l=disk number 0, 1, 2, 3
	ld 		h,0			;high order zero
	add		hl,hl		;*2
	add		hl,hl		;*4
	add		hl,hl		;*8
	add		hl,hl		;*16 (size of each header)
	ld 		de,dpbase
	add		hl,de		;hl=,dpbase (diskno*16) Note typo here in original source.
	ret
;
settrk:	;set track given by register c	
	ld 		a,c
	ld 		(track),a
	ret
;
setsec:	;set sector given by register c	
	ld 		a,c
	ld 		(sector),a
	ret
;
;
sectran:
	;translate the sector given by bc using the
	;translate table given by de	
	ex 		de,hl		;hl=.trans
	add		hl,bc		;hl=.trans (sector)
	ret					;debug no translation
	ld 		l,(hl)		;l=trans (sector)
	ld 		h,0			;hl=trans (sector)
	ret					;with value in hl
;
setdma:	;set dma address given by registers b and c
	ld 		l,c			;low order address
	ld 		h,b			;high order address
	ld 		(dmaad),hl	;save the address
	ret
;
read:
;Read one cp/M sector from disk.
;Return a 00h in register a if the operation completes properly, and 0lh if an error occurs during the read.
;Disk number in 'diskno'
;Track number in 'track'
;Sector number in 'sector'
;Dma address in 'dmaad' (0-65535)
;
			ld 		hl,hstbuf					;buffer to place disk sector (256 bytes)
rd_wait_BSY_reset:
			in 		a,(CF_REG7)					;check status
			and		80h							;check BSY bit
			jp 		nz,rd_wait_BSY_reset		;loop until not busy
rd_wait_RDY_set:
			in 		a,(CF_REG7)					;check	status
			and		40h							;check DRDY bit
			jp 		z,rd_wait_RDY_set			;loop until ready

			ld 		a,01h						;number of sectors = 1
			out		(CF_REG2),a					;sector count register
			ld 		a,(sector)					;sector
			out		(CF_REG3),a					;lba bits 0 - 7
			ld 		a,(track)					;track
			out		(CF_REG4),a					;lba bits 8 - 15
			ld 		a,(diskno)					;disk (only bits 
			out		(CF_REG5),a					;lba bits 16 - 23
			ld 		a,20h						;read sector command
			out		(CF_REG7),a

rd_wait_DRQ_set: 
			in 		a,(CF_REG7)					;read status
			and		08h							;DRQ bit
			jp 		z,rd_wait_DRQ_set			;loop until bit set

rd_wait_BSY_clear:	
			in 		a,(CF_REG7)
			and		80h
			jp 		nz,rd_wait_BSY_clear

read_loop:
			in 		a,(CF_REG0)					;get data
			ld 		(hl),a
			inc		hl
			in 		a,(CF_REG7)					;check status
			and		08h							;DRQ bit
			jp 		nz,read_loop				;loop until clear

			ld 		hl,(dmaad)					;memory location to place data read from disk
			ld 		de,hstbuf					;host buffer
			ld 		b,128						;size of CP/M sector
rd_memcpy:
			ld 		a,(de)						;get byte from host buffer
			ld 		(hl),a						;put in memory
			inc		hl
			inc		de
			djnz 	rd_memcpy					;put 128 bytes into memory
			in 		a,(CF_REG7)					;get status
			and		01h							;error bit
			ret

write:
;Write one cp/M sector to disk.
;Return a 00h in register a if the operation completes properly, and 0lh if an error occurs during the read or write
;Disk number in 'diskno'
;Track number in 'track'
;Sector number in 'sector'
;Dma address in 'dmaad' (0-65535)
			ld 		hl,(dmaad)			;memory location of data to write
			ld 		de,hstbuf			;host buffer
			ld 		b,128				;size of cp/M sector
wr_memcpy:
			ld 		a,(hl)				;get byte from memory
			ld 		(de),a				;put in host buffer
			inc		hl
			inc		de
			djnz 	wr_memcpy			;put 128 bytes in host buffer
			ld		hl,hstbuf			;location of data to write to disk
wr_wait_BSY_reset:
			in 		a,(CF_REG7)					;check status
			and		80h							;check BSY bit
			jp 		nz,wr_wait_BSY_reset			;loop until not busy
wr_wait_RDY_set:
			in 		a,(CF_REG7)					;check	status
			and		40h							;check DRDY bit
			jp 		z,wr_wait_RDY_set			;loop until ready
			ld 		a,01h						;number of sectors = 1
			out		(CF_REG2),a					;sector count register
			ld 		a,(sector)
			out		(CF_REG3),a					;lba bits 0 - 7 = "sector"
			ld 		a,(track)
			out		(CF_REG4),a					;lba bits 8 - 15 = "track"
			ld 		a,(diskno)
			out 	(CF_REG5),a					;lba bits 16 - 23, use 16 to 20 for "disk"
			ld 		a, 11100000b				;LBA mode, select drive 0
			out		(CF_REG6),a					;drive/head register
			ld 		a,30h						;Write sector command
			out		(CF_REG7),a
wr_wait_DRQ_set:
			in 		a,(CF_REG7)					;read status
			and		08h							;DRQ bit
			jp 		z,wr_wait_DRQ_set		;loop until bit set	
write_loop:
			ld 		a,(hl)
			out		(CF_REG0),a					;write data
			inc		hl
			in 		a,(CF_REG7)					;read status
			and		08h							;check DRQ bit
			jp 		nz,write_loop				;write until bit cleared
wr_wait_BSY_clear:
			in 		a,(CF_REG7)
			and		80h
			jp 		nz,wr_wait_BSY_clear
			in 		a,(CF_REG7)					;clear INTRQ
			and		01h							;check for error
			ret
;
;	the remainder of the cbios is reserved uninitialized
;	data area, and does not need to be a Part of the
;	system	memory image (the space must be available,
;	however, between"begdat" and"enddat").
;
track:	defs	2		;two bytes for expansion
sector:	defs	2		;two bytes for expansion
dmaad:	defs	2		;direct memory address
diskno:	defs	1		;disk number 0-15
;
;	scratch ram area for bdos use
begdat:	equ	$	 		;beginning of data area
dirbf:	defs	128	 	;scratch directory area
all00:	defs	31	 	;allocation vector 0
all01:	defs	31	 	;allocation vector 1
all02:	defs	31	 	;allocation vector 2
all03:	defs	31	 	;allocation vector 3
chk00:	defs	16		;check vector 0
chk01:	defs	16		;check vector 1
chk02:	defs	16	 	;check vector 2
chk03:	defs	16	 	;check vector 3
;
enddat:	equ	$	 		;end of data area
datsiz:	equ	$-begdat;	;size of data area
hstbuf: defs	256		;buffer for host disk sector
		defs 	128
biosstack: equ $

cpm_prompt_mesg:
	defb "\n"
	defb "         C P / M   version   2 . 2         \n"
	defb "Copyright (C)  1979 (c) by Digital Research\n", 0
